home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / src / haeberli / libgutil / arg.c < prev    next >
C/C++ Source or Header  |  1994-08-01  |  22KB  |  897 lines

  1. /*
  2.  * arg_parse: Command line argument parser.
  3.  *
  4.  * notable features:
  5.  *    arbitrary order of flag arguments
  6.  *    automatic argument conversion and type checking
  7.  *    multiple-character flag names
  8.  *    required, optional, and flag arguments
  9.  *    automatic usage message
  10.  *    subroutine call for exotic options (variable number of parameters)
  11.  *    modularized parsers encourage standardized options
  12.  *    expression evaluation
  13.  *    works either from argv or in interactive mode,
  14.  *        as a primitive language parser and interpreter
  15.  *    concise specification
  16.  *    easy to use
  17.  *
  18.  * Paul Heckbert    ph@miro.berkeley.edu
  19.  *
  20.  * 19 April 1988 - written at UC Berkeley
  21.  *
  22.  * simpler version written at Pacific Data Images, Aug 1985.
  23.  * Ideas borrowed from Ned Greene's ARGS at New York Inst. of Tech.
  24.  * and Alvy Ray Smith's AARG at Pixar.
  25.  */
  26.  
  27. static char rcsid[] = "$Header: arg.c,v 4.1 90/05/30 11:57:02 ph Locked $";
  28.  
  29. #include "varargs.h"
  30. #include "ctype.h"
  31. #include "string.h"
  32.  
  33. #include "simple.h"
  34. #include "arg.h"
  35.  
  36. #define CHECKTYPE(form, keyword) \
  37.     if (form->type!=0) { \
  38.     fprintf(stderr, "arg: %s doesn't belong in %s paramlist\n", \
  39.         keyword, form->format); \
  40.     return 0; \
  41.     } \
  42.     else
  43.  
  44. /* recognize a valid numeric constant or expression by its first char: */
  45. #define NUMERIC(s) (isdigit(*(s)) || \
  46.     *(s)=='.' || *(s)=='-' || *(s)=='+' || *(s)=='(')
  47.  
  48. int arg_debug = 0;        /* debugging level; 0=none */
  49. int arg_doccol = 24;        /* column at which to print doc string*/
  50. int arg_warning = 1;        /* print warnings about repeated flags? */
  51.  
  52. static Arg_form *regf;        /* advancing form ptr used by arg_find_reg */
  53.  
  54. va_list arg_doc_parse();
  55.  
  56. /* checkstr: check that s is a valid string */
  57. static checkstr(
  58. char *s, char *name, char *prev);
  59.  
  60. /* nargs: Count number of parameters in arg vector av before the next flag */
  61. static nargs(
  62.     int ac,
  63.     char **av,
  64.     Arg_form *f,
  65.     int *skip);
  66.  
  67. /* arg_form_print: print Arg_form as usage message to stderr */
  68. static arg_form_print(
  69.     Arg_form *form);
  70.  
  71. static scan(
  72.     int narg,
  73.     char **arg,
  74.     Arg_form *f);
  75.  
  76. static space(
  77.     FILE *fp,
  78.     int c, int c1);
  79. /*
  80.  * arg_parse(ac, av, varargs_list)
  81.  * Parse the arguments in av according to the varargs list, which contains
  82.  * format strings, parameter and subroutine ptrs, and other stuff.
  83.  * The varargs list must be terminated by a 0.
  84.  * Returns an error code.
  85.  */
  86.  
  87. arg_parse(va_alist)
  88. va_dcl
  89. {
  90.     char **av;
  91.     int ac, ret;
  92.     va_list ap;
  93.     Arg_form *form;
  94.  
  95.     va_start(ap);
  96.     ac = va_arg(ap, int);
  97.     av = va_arg(ap, char **);
  98.     if (ac<1 || ac>ARG_NARGMAX) {
  99.     fprintf(stderr,
  100.         "arg_parse: first arg to arg_parse (%d) doesn't look like argc\n",
  101.         ac);
  102.     return ARG_BADCALL;
  103.     }
  104.  
  105.     /* convert varargs to formlist */
  106.     form = arg_to_form1(ap);
  107.     if (!form)
  108.     return ARG_BADCALL;
  109.  
  110.     /* parse args according to form */
  111.     if (ac==2 && str_eq(av[1], "-stdin")) /* no args, run interactive version */
  112.     ret = arg_parse_stream(stdin, form);
  113.     else                /* args supplied, parse av */
  114.     ret = arg_parse_argv(ac, av, form);
  115.     return ret;
  116. }
  117.  
  118. /*----------------------------------------------------------------------*/
  119.  
  120. /*
  121.  * arg_to_form: convert varargs to formlist
  122.  * not called by arg_parse, but sometimes called from outside to build sublists
  123.  */
  124.  
  125. Arg_form *arg_to_form(va_alist)
  126. va_dcl
  127. {
  128.     va_list ap;
  129.  
  130.     va_start(ap);
  131.     return arg_to_form1(ap);
  132. }
  133.  
  134. /*
  135.  * arg_to_form1: convert varargs to formlist recursively.
  136.  * assumes va_start has already been called
  137.  * calls va_end when done to clean up
  138.  * returns 0 on error.
  139.  */
  140.  
  141. Arg_form *arg_to_form1(ap)
  142. va_list ap;
  143. {
  144.     char *s, *prevs;
  145.     int pi, t;
  146.     Arg_form *form, *prevform, *rootform;
  147.  
  148.     /*
  149.      * varargs syntax is:
  150.      *     formatstr [KEYWORD val] paramptr* docstr docargptr*
  151.      * where there are as many paramptrs as %'s in the format string
  152.      * and as many docargptrs as %'s in the doc string
  153.      */
  154.     rootform = 0;
  155.     prevs = "";
  156.     for (prevform=0; (s = va_arg(ap, char *)) != 0; prevform=form) {
  157.  
  158.     /* first, read the format string */
  159.     if (checkstr(s, "format string", prevs)) return 0;
  160.     ALLOC(form, Arg_form, 1);
  161.     form->next = 0;
  162.     form->format = s;
  163.     form->flag = 0;
  164.     form->type = 0;
  165.     form->param = 0;
  166.     form->parammask = 0;
  167.     form->subr = 0;
  168.     form->sublist = 0;
  169.     if (prevform) prevform->next = form;
  170.     else rootform = form;
  171.  
  172.     /* parse format to create flag and code strings, compute #params */
  173.     t = arg_format(form);
  174.     if (t) return 0;
  175.  
  176.     /* next, read the parameters and keywords */
  177.     pi = 0;
  178.     if (form->nparam>0) {
  179.         form->type = form->flag[0]=='-' ? ARG_PARAMFLAG : ARG_REGULAR;
  180.         assert(form->param = (int **)malloc(form->nparam*sizeof(int *)));
  181.     }
  182.     for (; (s = va_arg(ap, char *)) != 0;) {
  183.         /* note that we continue (not break) in all cases except one */
  184.         switch ((int)s) {
  185.         case ARG_FLAGNEXT:        /* ptr to flag vbl */
  186.             CHECKTYPE(form, "FLAG");
  187.             form->type = ARG_SIMPFLAG;
  188.             ALLOC(form->param, int *, 1);
  189.             *form->param = va_arg(ap, int *);
  190.             continue;
  191.         case ARG_SUBRNEXT:        /* ptr to action subr */
  192.             CHECKTYPE(form, "SUBR");
  193.             form->type = ARG_SUBRFLAG;
  194.             form->subr = (int (*)())va_arg(ap, int *);
  195.             /* append dots to end of format string */
  196.             assert(s = (char *)malloc(strlen(form->format)+5));
  197.             sprintf(s, "%s ...", form->format);
  198.             form->format = s;
  199.             continue;
  200.         case ARG_LISTNEXT:        /* ptr to sub-formlist */
  201.             CHECKTYPE(form, "SUBLIST");
  202.             form->type = ARG_SUBLISTFLAG;
  203.             form->sublist = va_arg(ap, Arg_form *);
  204.             continue;
  205.         default:            /* ptr to param */
  206.             if (pi>=form->nparam) break;
  207.             form->param[pi++] = (int *)s;
  208.             continue;
  209.         }
  210.         break;                /* end of params/keywords */
  211.     }
  212.  
  213.     if (!form->flag[0] && form->type==ARG_SUBLISTFLAG) {
  214.         fprintf(stderr, "arg: sublist must be given a flag name\n");
  215.         return 0;
  216.     }
  217.     if (!form->type)            /* just a doc string */
  218.         form->type = ARG_NOP;
  219.     /* finally, read the doc string */
  220.     if (checkstr(s, "doc string", form->format)) return 0;
  221.     form->doc = prevs = s;
  222.  
  223.     /* skip over doc args */
  224.     ap = arg_doc_parse(form, ap);
  225.     }
  226.     va_end(ap);
  227.     return rootform;
  228. }
  229.  
  230. /* checkstr: check that s is a valid string */
  231. static checkstr(
  232. char *s, char *name, char *prev)
  233. {
  234.     char *delim;
  235.  
  236.     delim = prev ? "\"" : "";
  237.     if (!s || (int)s&ARG_MASKNEXT) {
  238.     fprintf(stderr, "bad arg call: missing %s after %s%s%s\n",
  239.         name, delim, prev, delim);
  240.     return 1;
  241.     }
  242.     return 0;
  243. }
  244.  
  245. /*
  246.  * arg_format: parse the format string to create flag string,
  247.  * code string, parammask, and count the number of params.
  248.  * e.g.: format="-foo %d %F"  =>  flag="-size", code="dF", nparam=2
  249.  */
  250.  
  251. arg_format(f)
  252. Arg_form *f;
  253. {
  254.     char *s, *c;
  255.     int n, np;
  256.  
  257.     if (f->format[0]=='-') {        /* flag string present */
  258.     /* find the end of the flag string, put flag string in f->flag */
  259.     for (s= &f->format[1]; *s && *s!=' ' && *s!='%' && *s!='['; s++);
  260.     n = s-f->format;
  261.     assert(f->flag = (char *)malloc(n+1));
  262.     bcopy(f->format, f->flag, n);
  263.     f->flag[n] = 0;
  264.     }
  265.     else {
  266.     s = f->format;            /* no flag string: probably a reg arg */
  267.     f->flag = "";            /* or maybe a flagless subrflag */
  268.     }
  269.  
  270.     /* extract scanf codes from remainder of format string, put in f->code */
  271.     n = (f->format+strlen(f->format)-s)/2;    /* overestimate # of % codes */
  272.     assert(f->code = (char *)malloc(n+1));
  273.     for (c=f->code, np=0;; np++, s++) {
  274.     for (; *s==' ' || *s=='['; s++)
  275.         if (*s=='[') f->parammask |= 1<<np;
  276.     if (!*s || *s==']') break;
  277.     if (*s!='%' || !s[1]) {
  278.         fprintf(stderr, "arg: bad format string (%s)\n", f->format);
  279.         return ARG_BADCALL;
  280.     }
  281.     *c++ = *++s;
  282.     }
  283.     for (; *s; s++)
  284.     if (*s!=' ' && *s!=']') {
  285.         fprintf(stderr, "bad format (%s), nothing allowed after ']'s\n",
  286.         f->format);
  287.         return ARG_BADCALL;
  288.     }
  289.     f->parammask |= 1<<np;
  290.     if (np>=8*sizeof(int)) {
  291.     fprintf(stderr, "out of bits in parammask! too many params to %s\n",
  292.         f->flag);
  293.     return ARG_BADCALL;
  294.     }
  295.  
  296.     /* number of parameters to flag = number of '%'s in format string */
  297.     f->nparam = np;
  298.     *c = 0;
  299.     if (c-f->code!=f->nparam) fprintf(stderr, "OUCH!\n");
  300.     return 0;
  301. }
  302.  
  303. /*
  304.  * arg_doc_parse: Find the '%' format codes in f->doc and increment varargs
  305.  * ptr ap over the doc string parameters.  Updates f->doc to be the formatted
  306.  * documentation string and returns the new ap.
  307.  */
  308.  
  309. va_list arg_doc_parse(f, ap)
  310. Arg_form *f;
  311. va_list ap;
  312. {
  313.     char *s, buf[256];
  314.     int size, gotparam;
  315.     va_list ap0;
  316.  
  317.     ap0 = ap;
  318.     gotparam = 0;
  319.     for (s=f->doc; *s; s++) {
  320.     for (; *s; s++)            /* search for next format code */
  321.         if (s[0]=='%')
  322.         if (s[1]=='%') s++;    /* skip over %% */
  323.         else break;
  324.     if (!*s) break;
  325.     /* skip over numerical parameters */
  326.     for (s++; *s && *s=='-' || *s>'0'&&*s<='9' || *s=='.'; s++);
  327.     /* now *s points to format code */
  328.     switch (*s) {
  329.         case 'h': size = 0; s++; break;    /* half */
  330.         case 'l': size = 2; s++; break;    /* long */
  331.         default : size = 1;      break;    /* normal size */
  332.     }
  333.     if (!*s) {
  334.         fprintf(stderr, "arg: premature end of string in (%s)\n", f->doc);
  335.         break;
  336.     }
  337.     gotparam = 1;
  338.     /*
  339.      * simulate printf's knowledge of type sizes
  340.      * (it's too bad we have to do this)
  341.      */
  342.     switch (*s) {
  343.         case 'd': case 'D':
  344.         case 'o': case 'O':
  345.         case 'x': case 'X':
  346.         case 'c':
  347.         if (size==2 || *s>='A' && *s<='Z') va_arg(ap, long);
  348.         else va_arg(ap, int);
  349.         break;
  350.         case 'e':
  351.         case 'f':
  352.         case 'g':
  353.         /* note: float args are converted to doubles by MOST compilers*/
  354.         va_arg(ap, double);
  355.         break;
  356.         case 's':
  357.         va_arg(ap, char *);
  358.         break;
  359.         default:
  360.         fprintf(stderr, "arg: unknown format code %%%c in %s\n",
  361.             *s, f->doc);
  362.         va_arg(ap, int);
  363.         break;
  364.     }
  365.     }
  366.     if (gotparam) {    /* there are doc parameters, format a new doc string */
  367.     vsprintf(buf, f->doc, ap0);
  368.     assert(f->doc = (char *)malloc(sizeof(buf)+1));
  369.     strcpy(f->doc, buf);
  370.     }
  371.  
  372.     return ap;        /* varargs ptr past end of doc params */
  373. }
  374.  
  375. /*----------------------------------------------------------------------*/
  376.  
  377. #define LINEMAX 256
  378. #define ACMAX 128
  379.  
  380. typedef enum {SPACE, TOKEN, END} Token_type;
  381. static Token_type token_char();
  382.  
  383. /*
  384.  * arg_parse_stream: parse from an input stream (not from an arg vector)
  385.  * parse args in stream fp, line by line, according to formlist in form
  386.  * Returns 0 on success, negative on failure.
  387.  */
  388.  
  389. arg_parse_stream(fp, form)
  390. FILE *fp;
  391. Arg_form *form;
  392. {
  393.     char c, *av[ACMAX], line[LINEMAX], *p;
  394.     Token_type type;
  395.     int i, ac, ret, err;
  396.     Arg_form *oldregf;
  397.  
  398.     oldregf = regf;
  399.     regf = form;
  400.     arg_init(form);
  401.  
  402.     av[0] = "hi";
  403.     ret = 0;
  404.     for (;;) {                /* read and process line */
  405.     p = line;
  406.     while ((type = token_char(fp, &c))==SPACE);
  407.     for (ac=1; type!=END && ac<ACMAX;) {    /* split line into tokens */
  408.         av[ac++] = p;        /* save ptr to beginning of token */
  409.         do {
  410.         *p++ = c;
  411.         if (p >= line+LINEMAX) {
  412.             fprintf(stderr, "input line too long\n");
  413.             exit(1);
  414.         }
  415.         } while ((type = token_char(fp, &c))==TOKEN);
  416.         *p++ = 0;            /* terminate this token in line[] */
  417.         if (type==END) break;
  418.         while ((type = token_char(fp, &c))==SPACE);
  419.     }
  420.     if (feof(fp)) break;
  421.     if (arg_debug) {
  422.         fprintf(stderr, "ac=%d: ", ac);
  423.         for (i=1; i<ac; i++) fprintf(stderr, "(%s) ", av[i]);
  424.         fprintf(stderr, "\n");
  425.     }
  426.  
  427.     err = arg_parse_form1(ac, av, form);
  428.     if (!ret) ret = err;
  429.     }
  430.     if (!ret) ret = arg_done();
  431.     regf = oldregf;
  432.     return ret;
  433. }
  434.  
  435. /*
  436.  * token_char: is next char in stream fp part of a token?
  437.  * returns TOKEN if char in token, SPACE if whitespace, END if end of input line
  438.  * *p gets new char.
  439.  * handles quoted strings and escaped characters.
  440.  */
  441.  
  442. static Token_type token_char(fp, p)
  443. FILE *fp;
  444. char *p;
  445. {
  446.     int c, old_mode;
  447.     Token_type type;
  448.     static int mode = 0;    /* = '"' or '\'' if inside quoted string */
  449.  
  450.     type = TOKEN;
  451.     do {
  452.     old_mode = mode;
  453.     c = getc(fp);
  454.     switch (c) {
  455.         case EOF:
  456.         type = END;
  457.         break;
  458.         case '\\':
  459.         switch (c = getc(fp)) {
  460.             case 'b': c = '\b'; break;
  461.             case 'f': c = '\f'; break;
  462.             case 'n': c = '\n'; break;
  463.             case 'r': c = '\r'; break;
  464.             case 't': c = '\t'; break;
  465.             case 'v': c = '\v'; break;
  466.             case '0': c = '\0'; break;
  467.         }
  468.         break;
  469.         case '"':
  470.         switch (mode) {
  471.             case 0: mode = '"'; break;        /* begin " */
  472.             case '"': mode = 0; break;        /* end " */
  473.         }
  474.         break;
  475.         case '\'':
  476.         switch (mode) {
  477.             case 0: mode = '\''; break;        /* begin ' */
  478.             case '\'': mode = 0; break;        /* end ' */
  479.         }
  480.         break;
  481.         case '\n':
  482.         switch (mode) {
  483.             case 0: type = END; break;
  484.         }
  485.         break;
  486.     }
  487.     /* loop until we read a literal character */
  488.     } while (old_mode != mode);
  489.     *p = c;
  490.  
  491.     if (type!=END && mode==0 && (c==' ' || c=='\t' || c=='\n'))
  492.     type = SPACE;
  493.     return type;
  494. }
  495.  
  496. /*
  497.  * arg_parse_argv: do the actual parsing!
  498.  * parse the arguments in av according to the formlist in form
  499.  * Returns 0 on success, negative on failure.
  500.  */
  501.  
  502. arg_parse_argv(ac, av, form)
  503. int ac;
  504. char **av;
  505. Arg_form *form;
  506. {
  507.     int ret;
  508.     Arg_form *oldregf;
  509.  
  510.     oldregf = regf;
  511.     regf = form;
  512.     arg_init(form);
  513.     ret = arg_parse_form1(ac, av, form);
  514.     if (!ret) ret = arg_done();
  515.     regf = oldregf;
  516.     return ret;
  517. }
  518.  
  519. arg_parse_form1(ac, av, form)
  520. int ac;
  521. char **av;
  522. Arg_form *form;
  523. {
  524.     int i, di;
  525.     Arg_form *f;
  526.  
  527.     for (i=1; i<ac; i+=di) {
  528.     if (arg_debug)
  529.         fprintf(stderr, "arg %d: (%s)\n", i, av[i]);
  530.     if (av[i][0]=='-' && !NUMERIC(&av[i][1])) {    /* flag argument */
  531.         f = arg_find_flag(av[i], form);
  532.         if (!f) {
  533.         if (av[i][1])
  534.             fprintf(stderr, "unrecognized arg: %s\n", av[i]);
  535.         else        /* arg was "-"; print usage message */
  536.             arg_form_print(form);
  537.         return ARG_EXTRA;
  538.         }
  539.         di = arg_do(ac-i-1, &av[i+1], f);
  540.         if (di<0) return di;
  541.         di++;
  542.     }
  543.     else {            /* regular argument */
  544.         f = arg_find_reg();
  545.         if (!f) {
  546.         /* regular args exhausted, see if any flagless subrflags */
  547.         f = arg_find_flag("", form);
  548.         if (!f) {
  549.             fprintf(stderr, "extra arg: %s\n", av[i]);
  550.             return ARG_EXTRA;
  551.         }
  552.         }
  553.         di = arg_do(ac-i, &av[i], f);
  554.         if (di<0) return di;
  555.     }
  556.     }
  557.  
  558.     return 0;
  559. }
  560.  
  561. /*
  562.  * arg_init: initialize formlist before parsing arguments
  563.  * Set simple flags and repeat counts to 0.
  564.  */
  565.  
  566. arg_init(form)
  567. Arg_form *form;
  568. {
  569.     Arg_form *f;
  570.  
  571.     for (f=form; f; f=f->next)
  572.     if (f->type==ARG_SUBLISTFLAG) arg_init(f->sublist);    /* recurse */
  573.     else {
  574.         f->rep = 0;
  575.         if (f->type==ARG_SIMPFLAG) **f->param = 0;
  576.     }
  577. }
  578.  
  579. arg_done()
  580. {
  581.     for (; regf; regf=regf->next)    /* any required reg args remaining? */
  582.     if (regf->type==ARG_REGULAR && !(regf->parammask&1)) {
  583.         fprintf(stderr, "regular arg %s (%s) not set\n",
  584.         regf->format, regf->doc);
  585.         return ARG_MISSING;
  586.     }
  587.     return 0;
  588. }
  589.  
  590. /*
  591.  * arg_find_flag: find the flag matching arg in the form list (tree)
  592.  * returns form ptr if found, else 0
  593.  */
  594.  
  595. Arg_form *arg_find_flag(arg, form)
  596. char *arg;
  597. Arg_form *form;
  598. {
  599.     Arg_form *f, *t;
  600.  
  601.     for (f=form; f; f=f->next) {
  602.     if (f->type!=ARG_REGULAR && f->type!=ARG_NOP && str_eq(f->flag, arg))
  603.         return f;
  604.     if (f->type==ARG_SUBLISTFLAG) {
  605.         t = arg_find_flag(arg, f->sublist);        /* recurse */
  606.         if (t) return t;
  607.     }
  608.     }
  609.     return 0;
  610. }
  611.  
  612. /*
  613.  * arg_find_reg: find next regular argument
  614.  * each call advances the global pointer regf through the formlist
  615.  */
  616.  
  617. Arg_form *arg_find_reg()
  618. {
  619.     Arg_form *f;
  620.  
  621.     for (; regf; regf=regf->next) {
  622.     if (regf->type==ARG_REGULAR) {
  623.         f = regf;
  624.         regf = regf->next;
  625.         return f;
  626.     }
  627.     }
  628.     return 0;
  629. }
  630.  
  631. /*
  632.  * arg_do: process one form by parsing arguments in av according to the
  633.  * single form in f
  634.  *
  635.  * f was found by arg_find_flag or arg_find_reg,
  636.  *     so if f is a flag then we know av[-1] matches f->flag
  637.  *
  638.  * examine av[0]-av[ac-1] to determine number of parameters supplied
  639.  *     if simpleflag, set flag parameter and read no args
  640.  *     if subrflag, call subroutine on sub-args
  641.  *     if sublist, call arg_parse_form on sub-args
  642.  *     else it's a paramflag or regular arg, do arg-to-param assignments
  643.  * return number of arguments gobbled, or negative error code
  644.  */
  645.  
  646. arg_do(ac, av, f)
  647. int ac;
  648. char **av;
  649. Arg_form *f;
  650. {
  651.     int narg, skip, used, err, i;
  652.  
  653.     if (arg_debug)
  654.     av_print("  arg_do", ac, av);
  655.     if (f->type==ARG_SIMPFLAG || f->type==ARG_PARAMFLAG) {
  656.     /* don't complain about repeated subrflags or sublists */
  657.     assert(str_eq(av[-1], f->flag));
  658.     f->rep++;
  659.     if (f->rep>1 && arg_warning)
  660.         fprintf(stderr, "warning: more than one %s flag in arglist\n",
  661.         f->flag);
  662.     }
  663.  
  664.     narg = nargs(ac, av, f, &skip);
  665.  
  666.     used = 0;
  667.     switch (f->type) {
  668.     case ARG_SIMPFLAG:
  669.         **f->param = 1;
  670.         break;
  671.     case ARG_SUBRFLAG:
  672.         (*f->subr)(narg, av);
  673.         break;
  674.     case ARG_SUBLISTFLAG:
  675.         arg_parse_argv(narg+1, &av[-1], f->sublist);    /* recurse */
  676.         used = narg;
  677.         break;
  678.     default:            /* convert parameters */
  679.         err = scan(narg, av, f);
  680.         if (err) return err;
  681.         used = narg<f->nparam ? narg : f->nparam;
  682.         break;
  683.     }
  684.  
  685.     if ((f->type==ARG_REGULAR || f->type==ARG_PARAMFLAG) && used!=narg) {
  686.     fprintf(stderr, "warning: %d unused arg%s to %s: ",
  687.         narg-used, narg-used>1 ? "s" : "", av[-1]);
  688.     for (i=used; i<narg; i++)
  689.         fprintf(stderr, "%s ", av[i]);
  690.     fprintf(stderr, "\n");
  691.     }
  692.     return skip;
  693. }
  694.  
  695. /*
  696.  * nargs: Count number of parameters in arg vector av before the next flag.
  697.  * Arguments can be grouped and "escaped" using -{ and -}.
  698.  * NOTE: modifies av
  699.  * Returns number of valid args in new av.
  700.  * Sets *skip to number of args to skip in old av to get to next flag.
  701.  *
  702.  * This is the most complex code in arg_parse.
  703.  * Is there a better way?
  704.  *
  705.  * examples:
  706.  *     input:  ac=3, av=(3 4 -go)
  707.  *     output: av unchanged, skip=2, return 2
  708.  *
  709.  *     input:  ac=4, av=(-{ -ch r -})
  710.  *     output: av=(-ch r X X), skip=4, return 2
  711.  *
  712.  *     input:  ac=4, av=(-{ -foo -} -go)
  713.  *     output: av=(-foo X X -go), skip=3, return 1
  714.  *
  715.  *     input:  ac=6, av=(-{ -ch -{ -rgb -} -})
  716.  *     output: av=(-ch -{ -rgb -} X X), skip=6, return 4
  717.  *
  718.  *     where X stands for junk
  719.  */
  720.  
  721. static nargs(
  722.     int ac,
  723.     char **av,
  724.     Arg_form *f,
  725.     int *skip)
  726. {
  727.     char *flag, **au, **av0;
  728.     int i, j, level, die, np, mask, voracious;
  729.  
  730.     np = f->nparam;
  731.     mask = f->parammask;
  732.     flag = f->type==ARG_REGULAR ? f->format : f->flag;
  733.     voracious = f->type==ARG_SUBRFLAG || f->type==ARG_SUBLISTFLAG;
  734.     /* subrs&sublists want all the args they can get */
  735.  
  736.     level = 0;
  737.     av0 = au = av;
  738.     if (voracious) np = 999;
  739.     for (die=0, i=0; i<np && i<ac && !die; ) {
  740.     if (voracious) j = 999;
  741.     else for (j=i+1; !(mask>>j&1); j++);    /* go until we can stop */
  742.     /* try to grab params i through j-1 */
  743.     for (; i<j && i<ac || level>0; i++, au++, av++) {
  744.         if (au!=av) *au = *av;
  745.         if (str_eq(*av, "-{")) {
  746.         if (level<=0) au--;        /* skip "-{" in av if level 0 */
  747.         level++;            /* push a level */
  748.         }
  749.         else if (str_eq(*av, "-}")) {
  750.         level--;            /* pop a level */
  751.         if (level<=0) au--;        /* skip "-}" in av if level 0 */
  752.         if (level<0)
  753.             fprintf(stderr, "ignoring spurious -}\n");
  754.         }
  755.         else if (level==0 && av[0][0]=='-' && !NUMERIC(&av[0][1])) {
  756.         die = 1;        /* break out of both loops */
  757.         break;            /* encountered flag at level 0 */
  758.         }
  759.     }
  760.     }
  761.     if (arg_debug) {
  762.     fprintf(stderr, "    %s: requested %d, got %d args: ",
  763.         flag, np, au-av0);
  764.     for (j=0; j<au-av0; j++)
  765.         fprintf(stderr, "%s ", av0[j]);
  766.     fprintf(stderr, "\n");
  767.     }
  768.     *skip = i;
  769.     return au-av0;
  770. }
  771.  
  772. /*
  773.  * scan: call sscanf to read args into param array and do conversion
  774.  * returns error code (0 on success)
  775.  */
  776.  
  777. static scan(
  778.     int narg,
  779.     char **arg,
  780.     Arg_form *f)
  781. {
  782.     static char str[]="%X";
  783.     char *s;
  784.     int i, **p;
  785.     double x;
  786.  
  787.     if (f->nparam<narg) narg = f->nparam;
  788.     if (!(f->parammask>>narg&1)) {
  789.     fprintf(stderr, "you can't give %s just %d params\n",
  790.         f->format, narg);
  791.     return ARG_MISSING;
  792.     }
  793.     for (p=f->param, i=0; i<narg; i++, p++) {
  794.     str[1] = f->code[i];
  795.     switch (str[1]) {
  796.         case 'S':
  797.         /*
  798.          * dynamically allocate memory for string
  799.          * for arg_parse_argv: in case argv gets clobbered (rare)
  800.          * for arg_parse_stream: since line[] buffer is reused (always)
  801.          */
  802.         ALLOC(s, char, strlen(arg[i])+1);
  803.         strcpy(s, arg[i]);
  804.         *(char **)*p = s;
  805.         break;
  806.         case 's':           /* scanf "%s" strips leading, trailing blanks */
  807.         strcpy((char *)*p, arg[i]);
  808.         break;
  809.         case 'd':
  810.         *(int *)*p = expr_eval_int(arg[i]);
  811.         if (expr_error==EXPR_BAD) {    /* expression is garbage */
  812.             fprintf(stderr, "bad %s param\n", f->flag);
  813.             return ARG_BADARG;
  814.         }
  815.         break;
  816.         case 'D':
  817.         *(long *)*p = expr_eval_long(arg[i]);
  818.         if (expr_error==EXPR_BAD) {    /* expression is garbage */
  819.             fprintf(stderr, "bad %s param\n", f->flag);
  820.             return ARG_BADARG;
  821.         }
  822.         break;
  823.         case 'f': case 'F':
  824.         x = expr_eval(arg[i]);
  825.         if (expr_error==EXPR_BAD) {    /* expression is garbage */
  826.             fprintf(stderr, "bad %s param\n", f->flag);
  827.             return ARG_BADARG;
  828.         }
  829.         if (str[1]=='f') *(float *)*p = x;
  830.         else        *(double *)*p = x;
  831.         break;
  832.         default:
  833.         if (sscanf(arg[i], str, *p) != 1) {
  834.             fprintf(stderr, "bad %s param: \"%s\" doesn't match %s\n",
  835.             f->flag, arg[i], str);
  836.             return ARG_BADARG;
  837.         }
  838.         break;
  839.     }
  840.     }
  841.     return 0;                   /* return 0 on success */
  842. }
  843.  
  844. static char *bar = "==================================================\n";
  845.  
  846. /* arg_form_print: print Arg_form as usage message to stderr */
  847. static arg_form_print(
  848.     Arg_form *form)
  849. {
  850.     Arg_form *f;
  851.  
  852.     for (f=form; f; f=f->next) {
  853.     if (f->type!=ARG_NOP || f->format[0]) {
  854.         fprintf(stderr, "%s", f->format);
  855.         space(stderr, strlen(f->format), arg_doccol);
  856.     }
  857.     fprintf(stderr, "%s\n", f->doc);
  858.     if (arg_debug)
  859.         fprintf(stderr, "   %d (%s) [%s][%s]%x (%s)\n",
  860.         f->type, f->format, f->flag, f->code, f->parammask, f->doc);
  861.     if (f->type==ARG_SUBLISTFLAG) {
  862.         fprintf(stderr, bar);
  863.         arg_form_print(f->sublist);
  864.         fprintf(stderr, bar);
  865.     }
  866.     }
  867. }
  868.  
  869. /*
  870.  * space: currently in column c; tab and space over to column c1
  871.  * assumes 8-space tabs
  872.  */
  873.  
  874. static space(
  875.     FILE *fp,
  876.     int c, int c1)
  877. {
  878.     if (c>=c1) {
  879.     putc('\n', fp);
  880.     c = 0;
  881.     }
  882.     for (; c<c1&~7; c=(c+7)&~7) putc('\t', fp);
  883.     for (; c<c1; c++) putc(' ', fp);
  884. }
  885.  
  886. av_print(str, ac, av)
  887. int ac;
  888. char *str, **av;
  889. {
  890.     int i;
  891.  
  892.     fprintf(stderr, "%s: ", str);
  893.     for (i=0; i<ac; i++)
  894.     fprintf(stderr, "%s ", av[i]);
  895.     fprintf(stderr, "\n");
  896. }
  897.